package util

import (
	"fmt"
	"github.com/lionsoul2014/ip2region/binding/golang/xdb"
	"github.com/sirupsen/logrus"
	"net"
	"net/http"
	"strings"
)

func GetClientIp(req *http.Request) string {
	if req == nil {
		return "unknown"
	}
	ip := req.Header.Get("X-Forwarded-For")
	if ip == "" || strings.ToLower(ip) == "unknown" {
		ip = req.Header.Get("Proxy-Client-IP")
	}
	if ip == "" || strings.ToLower(ip) == "unknown" {
		ip = req.Header.Get("WL-Proxy-Client-IP")
	}
	if ip == "" || strings.ToLower(ip) == "unknown" {
		ip = req.Header.Get("X-Real-IP")
	}
	if ip == "" || strings.ToLower(ip) == "unknown" {
		ip = req.Header.Get("HTTP_CLIENT_IP")
	}
	if ip == "" || strings.ToLower(ip) == "unknown" {
		ip = req.Header.Get("HTTP_X_FORWARDED_FOR")
	}
	if ip == "" || strings.ToLower(ip) == "unknown" {
		ip, _, _ = net.SplitHostPort(req.RemoteAddr)
	}
	if ip == "0:0:0:0:0:0:0:1" || ip == "::1" || ip == "localhost" {
		ip = "127.0.0.1"
	}
	if ip != "" && len(ip) > 15 {
		if strings.Index(ip, ",") > 15 {
			ip = ip[0:strings.Index(ip, ",")]
		}
	}
	return ip
}

func GetLocationByIp(ip string) (location string) {
	if isInternalIp(ip) {
		return "内网IP"
	}

	var dbPath = "../data/ip2region.xdb"
	cBuff, err := xdb.LoadContentFromFile(dbPath)
	if err != nil {
		logrus.Errorf("failed to load content from `%s`: %s\n", dbPath, err)
		return
	}

	searcher, err := xdb.NewWithBuffer(cBuff)
	if err != nil {
		logrus.Errorf("failed to create searcher with content: %s\n", err)
		return
	}

	defer searcher.Close()

	region, err := searcher.SearchByStr(ip)
	if err != nil {
		logrus.Errorf("failed to SearchIP(%s): %s\n", ip, err)
		return
	}

	locations := strings.Split(region, "|")
	provinces := "北京|上海|天津|重庆|香港|澳门|台湾"
	if locations[0] != "中国" || strings.Contains(provinces, locations[2]) {
		location = fmt.Sprintf("%s·%s", locations[0], locations[2])
	} else {
		location = fmt.Sprintf("%s·%s·%s", locations[0], locations[2], locations[3])
	}
	location = strings.ReplaceAll(location, "·0", "")
	location = strings.ReplaceAll(location, "省", "")
	location = strings.ReplaceAll(location, "市", "")
	if location == "" {
		location = "unknown"
	}
	return
}

func isInternalIp(ip string) bool {
	if ip == "127.0.0.1" {
		return true
	}
	ipAddr := net.ParseIP(ip)
	privateIPBlocks := []*net.IPNet{
		// 10.0.0.0/8
		&net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(8, 32)},
		// 172.16.0.0/12
		&net.IPNet{IP: net.ParseIP("172.16.0.0"), Mask: net.CIDRMask(12, 32)},
		// 192.168.0.0/16
		&net.IPNet{IP: net.ParseIP("192.168.0.0"), Mask: net.CIDRMask(16, 32)},
	}

	for _, block := range privateIPBlocks {
		if block.Contains(ipAddr) {
			return true
		}
	}
	return false
}
